var createError = require("http-errors");
var express = require("express");
var path = require("path");
var cors = require("cors");
var cookieParser = require("cookie-parser");
var logger = require("morgan");
const expressjwt = require("express-jwt");
const jwt = require("jsonwebtoken");
let formidable = require("formidable");
let uploadDir = "nodeServer/uploads";
let fs = require("fs-extra");
let concat = require("concat-files");

var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
var orderRouter = require("./routes/order");
var uploadRouter = require("./routes/upload");
var customRouter = require("./routes/user");
var permissionsRouter = require("./routes/Permissions");
var commodityRouter = require("./routes/commodity");
var app = express();
app.use(cors());

// 设置白名单
app.use(
  expressjwt({
    secret: "123456",
    algorithms: ["HS256"],
  }).unless({
    path: [
      "/users/login",
      "/users/reset",
      "/users/resetPwd",
      "/updatePwd",
      "/users/verify-recaptcha",
      "/check/file",
      "/upload",
      "/merge",
      "/order/events",
      "/commodity/brandSearch",
      "/upload/uploads",
      /^\/routes\/uploads/, // 添加正则表达式到白名单
      "/users/get-role",
    ],
  })
);

//错误处理中间件，这里如果错误是UnauthorizedError，那么就证明accesstoken过期。
app.use((err, req, res, next) => {
  if (err) {
    // console.log(err);
    const accessToken = req.headers["authorization"];
    const refreshToken = req.headers["pass"];
    // console.log(!!accessToken, !!refreshToken);
    if (!!!accessToken && !!!refreshToken) {
      res.status(200).send({ code: 4001, msg: "无效的token" });
    }

    // 验证 Access Token
    if (accessToken) {
      jwt.verify(accessToken, "accessToken", (err, decoded) => {
        console.log("accessToken");
        if (err) {
          res
            .status(200)
            .send({ code: 4003, msg: "Access  Token 过期，请重新登录" });
        }
      });
    }

    if (refreshToken && !accessToken) {
      console.log(" refreshToken");
      jwt.verify(refreshToken, "refreshToken", (err, decodedRefreshToken) => {
        if (err) {
          // console.log("err.nameerr.nameerr.name=");
          res
            .status(200)
            .send({ code: 4006, msg: "Refresh Token 过期，请重新登录" });
        }
      });
    }
  } else {
    next();
  }
});

// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "jade");

app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));
app.use("/upload", express.static(path.join(__dirname, "upload")));
app.use(
  "/routes/uploads",
  express.static(path.join(__dirname, "routes/uploads"))
);

app.use("/", indexRouter);
app.use("/users", usersRouter);
app.use("/order", orderRouter);
app.use("/permissions", permissionsRouter);
app.use("/commodity", commodityRouter);
app.use("/upload", uploadRouter);
app.use("/user", customRouter);

// 列出文件夹下所有文件
function listDir(path) {
  return new Promise((resolve, reject) => {
    fs.readdir(path, (err, data) => {
      if (err) {
        reject(err);
        return;
      }
      // 把mac系统下的临时文件去掉
      if (data && data.length > 0 && data[0] === ".DS_Store") {
        data.splice(0, 1);
      }
      resolve(data);
    });
  });
}

// 文件或文件夹是否存在
function isExist(filePath) {
  return new Promise((resolve, reject) => {
    fs.stat(filePath, (err, stats) => {
      // 文件不存在
      if (err && err.code === "ENOENT") {
        resolve(false);
      } else {
        resolve(true);
      }
    });
  });
}

// 获取文件Chunk列表
async function getChunkList(filePath, folderPath, callback) {
  let isFileExit = await isExist(filePath);
  let result = {};
  // 如果文件已在存在, 不用再继续上传, 真接秒传
  if (isFileExit) {
    result = {
      stat: 1,
      file: {
        isExist: true,
        name: filePath,
      },
      desc: "file is exist",
    };
  } else {
    let isFolderExist = await isExist(folderPath);
    // 如果文件夹(md5值后的文件)存在, 就获取已经上传的块
    let fileList = [];
    if (isFolderExist) {
      fileList = await listDir(folderPath);
    }
    result = {
      stat: 1,
      chunkList: fileList,
      desc: "folder list",
    };
  }
  callback(result);
}

/**
 * 检查md5
 */
app.get("/check/file", (req, resp) => {
  let query = req.query;
  let fileName = query.fileName;
  let fileMd5Value = query.fileMd5Value;
  // 获取文件Chunk列表
  getChunkList(
    path.join(uploadDir, fileName),
    path.join(uploadDir, fileMd5Value),
    (data) => {
      resp.send(data);
    }
  );
});

app.all("/upload", (req, resp) => {
  const form = new formidable.IncomingForm({
    uploadDir: "nodeServer/tmp",
  });
  form.parse(req, function (err, fields, file) {
    // console.log(fields, file);
    let index = fields.index;
    let fileMd5Value = fields.fileMd5Value;
    let folder = path.resolve(__dirname, "nodeServer/uploads", fileMd5Value);
    folderIsExit(folder).then((val) => {
      let destFile = path.resolve(folder, fields.index);
      copyFile(file.data.path, destFile).then(
        (successLog) => {
          resp.send({
            stat: 1,
            desc: index,
          });
        },
        (errorLog) => {
          resp.send({
            stat: 0,
            desc: "Error",
          });
        }
      );
    });
  });

  // 文件夹是否存在, 不存在则创建文件
  function folderIsExit(folder) {
    return new Promise(async (resolve, reject) => {
      await fs.ensureDirSync(path.join(folder));
      resolve(true);
    });
  }
  // 把文件从一个目录拷贝到别一个目录
  function copyFile(src, dest) {
    let promise = new Promise((resolve, reject) => {
      fs.rename(src, dest, (err) => {
        if (err) {
          reject(err);
        } else {
          resolve("copy file:" + dest + " success!");
        }
      });
    });
    return promise;
  }
});

// 合并文件
async function mergeFiles(srcDir, targetDir, newFileName) {
  let fileArr = await listDir(srcDir);
  fileArr.sort((x, y) => {
    return x - y;
  });
  // 把文件名加上文件夹的前缀
  for (let i = 0; i < fileArr.length; i++) {
    fileArr[i] = srcDir + "/" + fileArr[i];
  }
  concat(fileArr, path.join(targetDir, newFileName), (err) => {
    if (err) {
      return false;
    }
    return true;
  });
}

// 合成
app.all("/merge", (req, resp) => {
  let query = req.query;
  let md5 = query.md5;
  let fileName = query.fileName;
  const res = mergeFiles(path.join(uploadDir, md5), uploadDir, fileName);
  resp.send({
    stat: res ? 1 : 0,
  });
});

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  console.log(err);
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get("env") === "development" ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render("error");
});

module.exports = app;
